package top.zhangjianyong.tools.service.api.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.queue.CircularFifoQueue;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.zhangjianyong.tools.entity.ETF;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.entity.marirui.JJLSKX;
import top.zhangjianyong.tools.model.*;
import top.zhangjianyong.tools.parm.KLineParam;
import top.zhangjianyong.tools.service.api.ETFDateService;
import top.zhangjianyong.tools.service.etf.ETFMairuiServiceImpl;
import top.zhangjianyong.tools.service.etf.ETFService;
import top.zhangjianyong.tools.service.etf.SohuETFServiceImpl;
import top.zhangjianyong.tools.service.mybatis.IETFDataService;
import top.zhangjianyong.tools.service.mybatis.IETFService;
import top.zhangjianyong.tools.service.mybatis.IJJLSKXService;
import top.zhangjianyong.tools.timer.ETFMedianTimer;
import top.zhangjianyong.tools.timer.ETFTimer;
import top.zhangjianyong.tools.utils.ApiResponse;
import top.zhangjianyong.tools.utils.ChronoUnitUtil;
import top.zhangjianyong.tools.utils.ETFMedianUtil;
import top.zhangjianyong.tools.utils.HolidayUtil;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;


/**
 * @author ： zhangjianyong
 * @date ： 2022/9/29 4:28 PM
 */
@Slf4j
@Service
public class ETFDateServiceImpl implements ETFDateService {


    @Autowired
    private IETFDataService ietfDataService;
    @Autowired
    private ETFMedianUtil etfMedianUtil;
    @Value("${etf.day}")
    private Integer etfDay;
    @Value("${etf.diff}")
    private BigDecimal maxDiff;
    @Autowired
    private HolidayUtil holidayUtil;
    @Autowired
    private IETFService ietfService;
    @Resource(type = ETFMairuiServiceImpl.class)
    private ETFService etfService;
    @Resource(type = SohuETFServiceImpl.class)
    private ETFService etfService2;

    @Autowired
    private IJJLSKXService ijjlskxService;
    @Autowired
    private ETFMedianUtil eTFMedianUtil;

    @Async
    @Transactional
    @Override
    public ApiResponse saveOrUpdate(String code) throws Exception {

        //更新最新值
        ETF etf = updateETF(code, 0);
        code = etf == null ? code : etf.getCode();
        List<ETFData> records = etfService.getRecords(code);
        if (CollectionUtil.isEmpty(records)) {
            log.info("[获取失败] code {}", code);
            return ApiResponse.fail("获取失败");
        }
        records.forEach(etfDate -> {
            ETFData etfData1 = ietfDataService.queryOne(etfDate.getCode(), etfDate.getValueDate());
            if (etfData1 == null) {
                ietfDataService.save(etfDate);
            } else {
                etfDate.setId(etfData1.getId());
                ietfDataService.updateById(etfDate);
            }
        });
        return ApiResponse.success();
    }


    @Override
    public void saveOrUpdatePage(String code) throws Exception {
        //更新最新值
        ETF etf = updateETF(code, 1);

        List<ETFData> records = etfService.getRecords(1, code);
        records.forEach(etfDate -> {
            ETFData etfData1 = ietfDataService.queryOne(etfDate.getCode(), etfDate.getValueDate());
            if (etfData1 == null) {
                etfDate.setName(etf.getName());
                ietfDataService.save(etfDate);
            } else {
                etfDate.setId(etfData1.getId());
                ietfDataService.updateById(etfDate);
            }
        });
    }

    /**
     * @param type 0-使用mairui 接口 1-使用sohu接口
     */
    public ETF updateETF(String code, int type) {
        try {
            ETF etf;
            if (type == 0) { //使用mairui接口
                etf = etfService.getETF(code);
            } else { //使用sohu接口
                etf = etfService2.getETF(code);
            }
            //etf.setCode(code);
            code = etf.getCode();
            //更新最新值
            ETF one = ietfService.getByCode(code);

            etf.setUpdateFlag(0);
            if (one == null) {
                etf.setHoldFlag(0);
                ietfService.save(etf);
            } else {
                etf.setId(one.getId());
                if (StrUtil.isBlank(etf.getName()))
                    etf.setName(one.getName());
                etf.setDelFlag(0);
                ietfService.updateById(etf);
            }
            return etf;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            ietfService.addUpdateFlag(code);
            return null;
        }


    }


    @Override
    public ApiResponse list() {
        List<Map<String, String>> list = ietfDataService.listEtf();
        return ApiResponse.success(list);
    }

    @Override
    public ApiResponse search(String code) {
        List<Map<String, String>> list = ietfDataService.search(code);
        return ApiResponse.success(list);
    }

    @Override
    public ApiResponse sameHis() {
        List<ETFData> list = ietfDataService.listOrderByDate("515790");
        LinkedBlockingQueue<ETFData> queue1 = new LinkedBlockingQueue<>(etfDay);
        LinkedBlockingQueue<ETFData> queue2 = new LinkedBlockingQueue<>(etfDay);
        List<ETFDiff> diffs = new ArrayList<>();
        for (int i = 0; i < list.size() - 6; i++) {
            queue1.add(list.get(i));
            for (int j = i + 1; j < list.size() - etfDay; j++) {
                if (queue2.size() == etfDay) {

                    List<ETFData> value1 = new ArrayList<>(queue1);
                    List<ETFData> value2 = new ArrayList<>(queue2);
                    //计算向量距离
                    double total = 0;
                    for (int k = 0; k < etfDay; k++) {
                        BigDecimal b1 = value1.get(k).getValue();
                        BigDecimal b2 = value2.get(k).getValue();

                        double pow = Math.pow(b1.subtract(b2).doubleValue(), 2);
                        total += pow;
                    }
                    double sqrt = Math.sqrt(total);

                    ETFDiff etfDiff = new ETFDiff();
                    etfDiff.setDate1(value1);
                    etfDiff.setDate2(value2);
                    etfDiff.setDiff(new BigDecimal(sqrt));
                    diffs.add(etfDiff);
                }

                queue2.add(list.get(j));
            }
        }

        List<ETFDiff> collect = diffs.stream()
            .sorted(Comparator.comparing(ETFDiff::getDiff))
            .collect(Collectors.toList());
        log.info(JSONObject.toJSONString(collect));

        return null;
    }

    public static void main(String[] args) {
        CircularFifoQueue<Integer> queue = new CircularFifoQueue<>(5);
        queue.add(1);
        queue.add(2);
        queue.add(3);
        System.out.println(queue.size());
        queue.add(4);
        queue.add(5);
        queue.add(6);
        System.out.println(JSONObject.toJSONString(queue));


    }

    /**
     * 计算向量距离
     */
    private ETFDiff getEtfDiff(CircularFifoQueue<ETFData> queue1, CircularFifoQueue<ETFData> queue2, List<ETFData> list, int i) {
        List<ETFData> value1 = new ArrayList<>(queue1);
        List<ETFData> value2 = new ArrayList<>(queue2);
        double total = 0;
        initAvgValue(value1);
        initAvgValue(value2);

        for (int k = 0; k < etfDay; k++) {
            BigDecimal b1 = value1.get(k).getAvgValue();
            BigDecimal b2 = value2.get(k).getAvgValue();

            double pow = Math.pow(b1.subtract(b2).doubleValue(), 2);
            total += pow;
        }
        double sqrt = Math.sqrt(total);
        ETFDiff etfDiff = new ETFDiff();
        etfDiff.setDate1(value1);
        etfDiff.setDate2(value2);
        etfDiff.setDiff(new BigDecimal(sqrt));
        List<ETFData> after1 = new ArrayList<>();
        for (int k = 0; k < etfDay; k++) {
            if (list.size() > (i + k + 1))
                after1.add(list.get(i + k + 1));

        }
        initAvgValue(after1);
        etfDiff.setAfterDate1(after1);
        return etfDiff;
    }

    private void initAvgValue(List<ETFData> dates) {
        BigDecimal avgValue = new BigDecimal(100);
        for (ETFData etfData : dates) {
            BigDecimal dailyRate = etfData.getDailyRate();
            dailyRate = dailyRate.divide(new BigDecimal(100), 4, BigDecimal.ROUND_HALF_UP).add(BigDecimal.ONE);
            avgValue = avgValue.multiply(dailyRate);
            etfData.setAvgValue(avgValue);
        }
    }

    public ETFTotalDiff todaySame(String code, boolean isCurrent) throws Exception {

        //取出所有记录
        List<ETFData> list = ietfDataService.listOrderByDate(code);
        //历史数据窗口
        CircularFifoQueue<ETFData> queue1 = new CircularFifoQueue<>(etfDay);
        //当前数据窗口
        CircularFifoQueue<ETFData> queue2 = new CircularFifoQueue<>(etfDay);
        for (int i = list.size() - etfDay; i < list.size(); i++) {
            queue2.add(list.get(i));
        }
        //获取当天
        ETFData etf = etfService.getETFDate(code, 0);
        if (isCurrent) { //实时价格加入计算
            queue2.add(etf);
        }


        //计算向量距离
        List<ETFDiff> diffs = new ArrayList<>();
        for (int i = 0; i < list.size() - 1; i++) {
            queue1.add(list.get(i));
            if (queue1.size() < etfDay) continue;

            ETFDiff etfDiff = getEtfDiff(queue1, queue2, list, i);
            diffs.add(etfDiff);

        }

        //截取距离最小前10个数据
        List<ETFDiff> resDiffs = diffs.stream()
            .filter(diff -> diff.getDiff().compareTo(maxDiff) < 0)
            .sorted(Comparator.comparing(ETFDiff::getDiff))
            //.limit(10)
            .collect(Collectors.toList());
        ETFTotalDiff etfTotalDiff = new ETFTotalDiff();
        if (CollectionUtil.isNotEmpty(resDiffs)) {


            //向量距离倒数和
            BigDecimal total = resDiffs.stream()
                .map(ETFDiff::getBackDiff)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            //计算综合增长率
            BigDecimal result = resDiffs.stream().map(etfDiff -> etfDiff.getBackDiff()
                    .divide(total, 4, BigDecimal.ROUND_HALF_UP)
                    .multiply(etfDiff.getAfterDate1().get(0).getDailyRate()))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            etfTotalDiff.setTotalRate(result);
        } else {
            etfTotalDiff.setTotalRate(BigDecimal.ZERO);
        }

        etfTotalDiff.setDiffs(resDiffs);

        etfTotalDiff.setCode(etf.getCode());
        etfTotalDiff.setName(etf.getName());
        return etfTotalDiff;
    }

    @Override
    public ApiResponse updateAll() throws Exception {
        etfTimer.update();
        /*List<ETF> etfs = ietfService.listNotDel();
        for (ETF etf : etfs) {
            String code = etf.getCode();
            saveOrUpdate(code);
        }*/
        return ApiResponse.success();
    }

    @Override
    public ApiResponse down3Rate() {
        List<ETF> etfs = ietfService.listNotDel();
        for (ETF etf : etfs) {
            String code = etf.getCode();
            etfMedianUtil.update3DownRate(code);
        }
        return ApiResponse.success();
    }

    @Autowired
    private ETFTimer etfTimer;
    @Autowired
    private ETFMedianTimer etfMedianTimer;

    @Override
    public ApiResponse doTimerStart() {
        etfTimer.timerStart();
        return ApiResponse.success();
    }

    @Override
    public ApiResponse doTimerEnd() {
        etfTimer.timerEnd();
        return ApiResponse.success();
    }

    @Override
    public ApiResponse doTimerMonth() {
        etfMedianTimer.doTimer();
        return ApiResponse.success();
    }

    @Override
    public ApiResponse median() {
        return median(ChronoUnit.MONTHS, -1);
    }

    private ApiResponse median(ChronoUnit unit, int num) {

        List<ETF> list = checkData();
        List<ETFMedianInfo> collect = etfMedianUtil.etfMedianInfo(list, unit, num);
        return ApiResponse.success(collect);
    }

    public ApiResponse median(String unitName, int num) {
        ChronoUnit unit = ChronoUnitUtil.getByName(unitName);
        return median(unit, num);
    }

    @Override
    public ApiResponse delivery(EtfDeliveryQuery rawQuery) {
        EtfDeliveryQuery query = rawQuery == null ? new EtfDeliveryQuery() : rawQuery;
        query.normalize();
        ChronoUnit unit = resolveUnit(query.getUnit());
        List<ETF> list = checkData();
        List<ETFMedianInfo> infos = etfMedianUtil.etfMedianInfo(list, unit, query.getNum());

        List<ETFMedianInfo> filtered = infos.stream()
            .filter(info -> filterByCode(info, query.getCode()))
            .filter(info -> filterByHold(info, query))
            .collect(Collectors.toList());

        Comparator<ETFMedianInfo> comparator = buildComparator(query);
        if (comparator != null) {
            filtered.sort(comparator);
        }

        List<EtfDeliveryItem> items = new ArrayList<>(filtered.size());
        for (int i = 0; i < filtered.size(); i++) {
            items.add(toDeliveryItem(filtered.get(i), i + 1));
        }

        EtfDeliverySummary summary = buildSummary(filtered, query);
        EtfDeliveryResult result = new EtfDeliveryResult();
        result.setItems(items);
        result.setSummary(summary);
        result.setFilters(buildFilters());
        result.setTimestamp(LocalDateTime.now());
        return ApiResponse.success(result);
    }

    private ChronoUnit resolveUnit(String unitName) {
        try {
            return ChronoUnitUtil.getByName(unitName);
        } catch (Exception e) {
            return ChronoUnit.WEEKS;
        }
    }

    private boolean filterByCode(ETFMedianInfo info, String keyword) {
        if (StrUtil.isBlank(keyword)) {
            return true;
        }
        String lowerKeyword = keyword.toLowerCase();
        return (info.getCode() != null && info.getCode().toLowerCase().contains(lowerKeyword))
            || (info.getName() != null && info.getName().toLowerCase().contains(lowerKeyword));
    }

    private boolean filterByHold(ETFMedianInfo info, EtfDeliveryQuery query) {
        Integer holdFlag = query.getHoldFlag();
        if (holdFlag != null) {
            return Objects.equals(holdFlag, info.getHoldFlag());
        }
        if (Boolean.TRUE.equals(query.getOnlyHolding())) {
            return info.getHoldFlag() != null && info.getHoldFlag() != 0;
        }
        return true;
    }

    private Comparator<ETFMedianInfo> buildComparator(EtfDeliveryQuery query) {
        String key = StrUtil.isNotBlank(query.getSortKey()) ? query.getSortKey() : defaultSortKey(query.getType());
        boolean desc = isDesc(query.getSortDir(), key);
        Comparator<ETFMedianInfo> comparator;
        switch (key) {
            case "rsi":
                comparator = Comparator.comparing(ETFMedianInfo::getRsi, Comparator.nullsLast(BigDecimal::compareTo));
                break;
            case "updown":
                comparator = Comparator.comparing(ETFMedianInfo::getLastUpDown, Comparator.nullsLast(BigDecimal::compareTo));
                break;
            case "rang":
                comparator = Comparator.comparing(ETFMedianInfo::getRang, Comparator.nullsLast(BigDecimal::compareTo));
                break;
            case "hcf":
                comparator = Comparator.comparing(ETFMedianInfo::getHcf, Comparator.nullsLast(BigDecimal::compareTo));
                break;
            case "position":
            default:
                comparator = Comparator.comparing(ETFMedianInfo::getPosition, Comparator.nullsLast(BigDecimal::compareTo));
                break;
        }
        return desc ? comparator.reversed() : comparator;
    }

    private boolean isDesc(String sortDir, String key) {
        if (StrUtil.isNotBlank(sortDir)) {
            return "desc".equalsIgnoreCase(sortDir);
        }
        switch (key) {
            default:
                return false;
        }
    }

    private String defaultSortKey(Integer type) {
        if (type == null) {
            return "position";
        }
        switch (type) {
            case 6:
                return "rsi";
            case 7:
                return "updown";
            case 1:
            default:
                return "position";
        }
    }

    private EtfDeliveryItem toDeliveryItem(ETFMedianInfo info, int rank) {
        EtfDeliveryItem item = new EtfDeliveryItem();
        item.setCode(info.getCode());
        item.setName(info.getName());
        item.setValue(info.getValue());
        item.setDailyRate(info.getDailyRate());
        item.setDateTime(info.getDateTime());
        item.setRang(info.getRang());
        item.setPosition(info.getPosition());
        item.setHcf(info.getHcf());
        item.setSlope(info.getSlope());
        item.setRsi(info.getRsi());
        item.setLastUpDown(info.getLastUpDown());
        item.setVolumeRatio(info.getVolumeRatio());
        item.setPredict(info.getPredict());
        item.setPredictRate(info.getPredictRate());
        item.setHoldFlag(info.getHoldFlag());
        item.setHoldFlagText(resolveHoldFlagText(info.getHoldFlag()));
        item.setDays(info.getDays());
        item.setStartDate(info.getStartDate());
        item.setEndDate(info.getEndDate());
        item.setRank(rank);
        return item;
    }

    private String resolveHoldFlagText(Integer holdFlag) {
        if (holdFlag == null || holdFlag == 0) {
            return "未持仓";
        }
        if (holdFlag == 3) {
            return "持仓";
        }
        return "观察";
    }

    private EtfDeliverySummary buildSummary(List<ETFMedianInfo> filtered, EtfDeliveryQuery query) {
        EtfDeliverySummary summary = new EtfDeliverySummary();
        summary.setTotal(filtered.size());
        summary.setHoldCount((int) filtered.stream()
            .filter(info -> info.getHoldFlag() != null && info.getHoldFlag() != 0)
            .count());
        summary.setAvgPosition(calculateAvgPosition(filtered));
        summary.setUnit(query.getUnit());
        summary.setNum(query.getNum());
        summary.setType(query.getType());
        summary.setSortKey(StrUtil.isNotBlank(query.getSortKey()) ? query.getSortKey() : defaultSortKey(query.getType()));
        summary.setSortDir(StrUtil.isNotBlank(query.getSortDir()) ? query.getSortDir() : (isDesc(null, summary.getSortKey()) ? "desc" : "asc"));
        summary.setGeneratedAt(LocalDateTime.now());
        return summary;
    }

    private BigDecimal calculateAvgPosition(List<ETFMedianInfo> filtered) {
        List<BigDecimal> positions = filtered.stream()
            .map(ETFMedianInfo::getPosition)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
        if (positions.isEmpty()) {
            return BigDecimal.ZERO;
        }
        BigDecimal total = positions.stream()
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        return total.divide(BigDecimal.valueOf(positions.size()), 2, RoundingMode.HALF_UP);
    }

    private EtfDeliveryFilters buildFilters() {
        EtfDeliveryFilters filters = new EtfDeliveryFilters();
        filters.setUnitOptions(UNIT_OPTIONS);
        filters.setTypeOptions(TYPE_OPTIONS);
        filters.setSortOptions(SORT_OPTIONS);
        return filters;
    }

    private static final List<EtfFilterOption> UNIT_OPTIONS = Collections.unmodifiableList(Arrays.asList(
        new EtfFilterOption("年", "years"),
        new EtfFilterOption("月", "months"),
        new EtfFilterOption("周", "weeks"),
        new EtfFilterOption("日", "days")
    ));

    private static final List<EtfFilterOption> TYPE_OPTIONS = Collections.unmodifiableList(Arrays.asList(
        new EtfFilterOption("位置", "1"),
        new EtfFilterOption("RSI", "6"),
        new EtfFilterOption("涨跌", "7")
    ));

    private static final List<EtfFilterOption> SORT_OPTIONS = Collections.unmodifiableList(Arrays.asList(
        new EtfFilterOption("位置", "position"),
        new EtfFilterOption("RSI", "rsi"),
        new EtfFilterOption("涨跌", "updown"),
        new EtfFilterOption("区间", "rang"),
        new EtfFilterOption("回撤", "hcf")
    ));

    @Override
    public ApiResponse updateNew() {
        etfTimer.updateNewNow();
        return ApiResponse.success();
    }

    @Override
    public ApiResponse hold(String code, Integer flag) {
        ietfService.setHoldFlag(code, flag);
        return ApiResponse.success();
    }

    @Override
    public ApiResponse remove(String code) {
        if (ietfService.removeByCode(code)) {
            return ApiResponse.success();
        }
        return ApiResponse.fail("删除失败");
    }

    @Override
    public ApiResponse listAllData(int limit) {
        List<ETF> etfs = ietfService.listNotDel();
        List<ETFVO> etfvos = etfs.stream().map(e -> {
            ETFVO etfvo = new ETFVO();
            BeanUtils.copyProperties(e, etfvo);
            return etfvo;
        }).collect(Collectors.toList());
        for (ETFVO etf : etfvos) {
            String code = etf.getCode();
            List<JJLSKX> list = ijjlskxService.listByCode(code, limit);
            etf.setKData(list);
        }
        return ApiResponse.success(etfvos);
    }

    @Override
    public ApiResponse listAll() {
        List<ETF> etfs = ietfService.listNotDel();
        return ApiResponse.success(etfs);
    }

    @Override
    public ApiResponse getKLine(List<KLineParam> params) {
        List<JJLSKX> result = new ArrayList<>();
        for (KLineParam param : params) {
            List<JJLSKX> list = ijjlskxService.listByCode(param.getCode(), param.getLimit());
            result.addAll(list);
        }
        return ApiResponse.success(result);
    }

    private List<ETF> checkData() {
        //取三个个交易日时间
        List<LocalDate> dates = holidayUtil.lastStock(LocalDate.now(), 3);
        log.info("检查交易日：{}", dates);
        List<ETF> list = ietfService.listNotDel();
        Set<String> codeSet = list.stream().map(ETF::getCode).collect(Collectors.toSet());
        //检查历史数据完整
        String code = etfMedianUtil.checkDateFail(codeSet, dates);
        if (StrUtil.isNotBlank(code)) {
            etfTimer.update();
            throw new RuntimeException("历史数据未更新，请检查:" + code);
        }
        //检查是否更新失败
        if (ietfService.existUpdateFail(5)) {
            etfTimer.updateNewNow();
            throw new RuntimeException("更新净值失败");
        }
        return list;
    }

    /**
     * 根据传入的类型获取ETF机会列表
     * <p>
     * type参数说明：
     * "1" - 返回符合机会条件的ETF列表（isOpportunity为true）
     * "2" - 返回符合机会条件V2的ETF列表（isOpportunityV2为true）
     * 其他 - 返回所有ETF的机会信息
     *
     * @param type 类型参数，决定返回的机会列表的过滤方式
     * @return ApiResponse 包含机会列表的响应
     */
    public ApiResponse opportunity(String type) {
        List<ETF> list = checkData(); // 检查数据的完整性和有效性
        List<ETFOpportunity> res = list.stream().map(eTFMedianUtil::etfOpportunity)
            .collect(Collectors.toList()); // 获取所有ETF的机会信息
        if ("1".equals(type)) {
            // 过滤出isOpportunity为true的ETF机会
            List<ETFOpportunity> collect = res.stream()
                .filter(ETFOpportunity::isOpportunity)
                .collect(Collectors.toList());
            return ApiResponse.success(collect);
        } else if ("2".equals(type)) {
            // 过滤出isOpportunityV2为true的ETF机会
            List<ETFOpportunity> collect = res.stream()
                .filter(ETFOpportunity::isOpportunityV2)
                .collect(Collectors.toList());
            return ApiResponse.success(collect);
        }
        // 默认返回所有ETF的机会信息
        return ApiResponse.success(res);
    }

    @Override
    public ApiResponse getData(List<KLineParam> params) {
        List<ETFVO> list = new ArrayList<>();
        for (KLineParam param : params) {
            ETF etf = ietfService.getByCode(param.getCode());
            ETFVO etfvo = new ETFVO();
            BeanUtils.copyProperties(etf, etfvo);
            //获取历史k线数据
            List<JJLSKX> jjlskxes = ijjlskxService.listByCode(param.getCode(), param.getLimit());
            etfvo.setKData(jjlskxes);
            list.add(etfvo);
        }

        return ApiResponse.success(list);
    }
}
